/*
* ============================================================================
*  Name        : sea_list.cpp
*  Part of     : Finite state machine utilities
*  Description : List implementation
*  Version     : %version: %
*  
*
* ============================================================================
*/
#include "sea_list.h"
#include "sea_iterator.h"
#include "sea_trace.h"

/**
 * @file sea_list.cpp
 * @brief
 * List of objects by a particular association. \b Thread \b safe. <br>
 * Ref counters view of an element in the list: <br>
 * All objects in the list have rc == 1. \b First and \b last links have rc == 2, other links have rc = 3
 *
 * @ingroup AssociationUtilities
 */
using namespace sea;

// -------------------------------------------------------------------------------------------------
// Attach new_obj on the RIGHT of existing in the list obj
// -------------------------------------------------------------------------------------------------
bool List::attach_right_of( Object* obj, Object* new_obj )
{
    if ( !new_obj || !obj )
    {
        return false;
    }

    OsMutex::Scoped safe( &m_lock );

    if ( !new_obj->attach_right_of( obj, &m_id ) ) 
    {
        return false;
    }

    m_size++;

    if ( m_last == obj ) 
    {
        // Release last element reference
        m_last = new_obj;
    }

    return true;
}

// -------------------------------------------------------------------------------------------------
// Attach new_obj on the LEFT of obj
// -------------------------------------------------------------------------------------------------
bool List::attach_left_of( Object* new_obj, Object* obj )
{
    if ( !new_obj || !obj )
    {
        return false;
    }

    OsMutex::Scoped safe( &m_lock );

    if ( !new_obj->attach_left_of( obj, &m_id ) ) 
    {
        return false;
    }

    m_size++;

    if ( m_first == obj ) 
    {
        // Release first element reference
        m_first = new_obj;
    }

    return true;
}

// -------------------------------------------------------------------------------------------------
// Insert new_obj on the RIGHT of existing in the list obj
// -------------------------------------------------------------------------------------------------
bool List::insert_right_of( Object* obj, Object* new_obj )
{
    if ( !new_obj || !obj )
    {
        return false;
    }

    OsMutex::Scoped safe( &m_lock );

    if ( !new_obj->insert_right_of( obj, &m_id ) ) 
    {
        return false;
    }

    m_size++;

    if ( m_last == obj ) 
    {
        // Release last element reference
        m_last = new_obj;
    }

    return true;
}

// -------------------------------------------------------------------------------------------------
// Attach new_obj on the LEFT of obj
// -------------------------------------------------------------------------------------------------
bool List::insert_left_of( Object* new_obj, Object* obj )
{
    if ( !new_obj || !obj )
    {
        return false;
    }

    OsMutex::Scoped safe( &m_lock );

    if ( !new_obj->insert_left_of( obj, &m_id ) ) 
    {
        return false;
    }

    m_size++;

    // Was it first?
    if ( m_first == obj ) 
    {
        // Release first element reference
        m_first = new_obj;
    }

    return true;
}

// -------------------------------------------------------------------------------------------------
// Attach as a FIRST object in the list
// -------------------------------------------------------------------------------------------------
bool List::attach_first( Object* obj, bool unique /* = false */ )
{
    if ( !obj )
    {
        return false;
    }
    
    OsMutex::Scoped safe( &m_lock );

    if ( m_first != NULL ) 
    {
        if ( !obj->attach_left_of( m_first.get( ), &m_id, unique ) )
        {
            return false;
        }
    }

    m_first = obj;

    if ( m_last == NULL )
    {
        ASSERT_DEBUG( m_size == 0 );
        m_last = obj;
    }
    m_size++;

    return true;
}

// -------------------------------------------------------------------------------------------------
// Attach as a LAST object in the list
// -------------------------------------------------------------------------------------------------
bool List::attach_last( Object* obj, bool unique /* = false */ )
{
    if ( !obj )
    {
        return false;
    }
    
    OsMutex::Scoped safe( &m_lock );

    if ( m_last != NULL ) 
    {
        if ( !obj->attach_right_of( m_last.get( ), &m_id, unique ) )
        {
            return false;
        }
    }

    m_last = obj;

    if ( m_first == NULL ) 
    {
        ASSERT_DEBUG( m_size == 0 );
        m_first = obj;
    }
    m_size++;

    return true;
}

// -------------------------------------------------------------------------------------------------
// Remove object from the list
// -------------------------------------------------------------------------------------------------
bool List::detach( Object* obj )
{
    OsMutex::Scoped safe( &m_lock );

    if ( !obj || m_size == 0 )
    {
        return false;
    }

    // Check if obj belongs to the list
    if ( m_size == 1 ) 
    {
        ASSERT_DEBUG( m_first != NULL );
        ASSERT_DEBUG( m_first == m_last );
        if ( m_first != obj )
        {
            return false;
        }
    } 
    else 
    {
        sea::ref< Link > link = obj->get_link( m_id );
        if ( link == NULL )
        {
            return false;
        }
    }

    if ( m_first == obj ) 
    {
        m_first = obj->get_right_obj( m_id );
    }
    
    if ( m_last == obj )
    {
        m_last = obj->get_left_obj( m_id );
    }

    m_size--;
    if ( m_size )
    {
        obj->detach( m_id );
    }

    return true;
}

// -------------------------------------------------------------------------------------------------
// Release all elements from the list
// -------------------------------------------------------------------------------------------------
void List::clear( )
{
    OsMutex::Scoped safe( &m_lock );

    if ( m_first == NULL )
    {
        ASSERT_DEBUG( m_last == NULL && m_size == 0 );
        return;
    }
    
    m_id.reset( );

    Object* next;
    HIterator< > it( m_first.get( ), m_id );

    for ( Object* obj = *it; obj; obj = next ) 
    {
        Association id = it.association( );
        it++;
        next = *it;
        obj->detach( id );
    }

    m_first = NULL;
    m_last = NULL;

    m_size = 0;
}

// -------------------------------------------------------------------------------------------------
